/*
  This file is part of JCM.

  JCM is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  JCM is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with JCM.  If not, see <http://www.gnu.org/licenses/>.
*/
import java.awt.event.*;
import java.awt.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;
import com.toedter.calendar.*;

/**
 * Implementação de editor de células usando campo de texto formatado
 * para editar valores tipo Date.
 * A formatação das datas usa Locale pt-BR no formato médio (dd/mm/aaaa)
 * limitando entrada de datas num intervalo fornecido.
*/
public class DateEditor extends DefaultCellEditor
{
  /**
   * Construtor da classe especializada na entrada de valores tipo Date.
   *
   * @param minDate Data mínima permitida.
   * @param maxDate Date máxima permitida.
  */
  public DateEditor(Date minDate, Date maxDate)
  {
    super(new JTextFieldDateEditor());

    ftf = (JFormattedTextField) getComponent();
    minimum = minDate;
    maximum = maxDate;

    DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, DateUtils.localeBR);
    DateFormatter dateFormatter = new DateFormatter(dateFormat);
    /* dateFormatter.setFormat(dateFormat); */
    dateFormatter.setMinimum(minimum);
    dateFormatter.setMaximum(maximum);

    ftf.setFormatterFactory(new DefaultFormatterFactory(dateFormatter));
    ftf.setValue(minimum);
    ftf.setHorizontalAlignment(JTextField.CENTER);
    ftf.setFocusLostBehavior(JFormattedTextField.PERSIST);

    ftf.getInputMap().put(KeyStroke.getKeyStroke("Enter"), "COMMIT");
    ftf.getActionMap().put("COMMIT",
      new AbstractAction() {
        public void actionPerformed(ActionEvent e)
        {
          if (!ftf.isEditValid()) {
            if (userSaysRevert()) {
              ftf.postActionEvent();
            }
          } else {
            try {
              ftf.commitEdit();
              ftf.postActionEvent();
            } catch (java.text.ParseException exc) {
            }
          }
        }
        private static final long serialVersionUID = 6454835466268912975L;
    });
  }

  /**
   * Invoca o método que assinala valor no campo de texto formatado.
   *
   * @param table Componente pai da célula que usa esse editor.
   * @param value Objeto a assinalar para a célula.
   * @param isSelected Status de seleção da célula.
   * @param row Número da linha da célula.
   * @param column Número da coluna da célula.
  */
  @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
  {
    final JFormattedTextField f = (JFormattedTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);
    f.setValue(value);
    // bug (#4192625) fix
    f.selectAll();
    f.getCaret().setVisible(true);
    EventQueue.invokeLater(new Runnable(){
      public void run() { f.requestFocus(); }
    });
    return f;
  }

  /**
   * Acessa o objeto associado à célula que usa esse editor.
   *
   * @return Objeto associado à célula que usa esse editor se for do tipo
   *         Double e em caso contrário retorna null.
  */
  @Override public Object getCellEditorValue()
  {
    JFormattedTextField f = (JFormattedTextField) getComponent();
    Object o = f.getValue();
    if (o instanceof Date) {
      return o;
    } else if (o instanceof Date) {
      return (Date) o;
    } else {
      return null;
    }
  }

  /**
   * Encerra a edição e atualiza o valor associado ao campo se o texto
   * editado é válido e em caso contrário notifica o usuário sobre o fato
   * oferecendo as opções de continuar a edição ou retornar o valor prévio.
   * Nota: Quando o texto editado é válido, sua super-classe é invocada
   *       para notificar os listeners interessados no evento.
   *
   * @return Status do encerramento de edição do campo.
  */
  @Override public boolean stopCellEditing()
  {
    JFormattedTextField f = (JFormattedTextField) getComponent();
    if (f.isEditValid()) {
      try {
        f.commitEdit();
      } catch (java.text.ParseException e) {
      }
    } else if (!userSaysRevert()) { // text is invalid user wants to edit
      return false;                 // don't let the editor go away
    }
    return super.stopCellEditing();
  }

  /**
   * Avisa o usuário que o texto digitado é inválido.
   *
   * @return <b>true</b> se o usuário opta por reverter ao valor válido
   *         prévio ou <b>false</b> se opta por continuar a edição.
  */
  protected boolean userSaysRevert()
  {
    Toolkit.getDefaultToolkit().beep();
    ftf.selectAll();
    Object[] options = {"Editar", "Reverter"} ;
    String message = String.format("<html><center>Espera-se uma data entre <b>%1$td/%1$tm/%1$tY</b> e <b>%2$td/%2$tm/%2$tY</b>.<br>Opte por continuar editando ou reverter para o valor prévio.</center></html>", minimum, maximum);
    int answer = JOptionPane.showOptionDialog(
      SwingUtilities.getWindowAncestor(ftf), message,
      "Texto Inválido",
      JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null,
      options, options[1]);
    if (answer == 1) {
      ftf.setValue(ftf.getValue());
      return true;
    }
    return false;
  }

  private JFormattedTextField ftf;

  private Date minimum;

  private Date maximum;

  private static final long serialVersionUID = -5873159331470570615L;
}